﻿#include <QTest>
#include <math.h>
#include <functional>
#include "test_benchmark.h"
#include "JZContainer.h"
#include "JZNodeFunction.h"
#include "JZNodeFlow.h"
#include "JZNodeValue.h"

//BenchmarkTest
BenchmarkTest::BenchmarkTest()
{

}

void BenchmarkTest::testBase()
{   
    return;
    auto env = m_project.environment();
    QList<QVariant> varList;
    QMap<int,QVariant> varMap;
    QMap<QString,QVariant> varStrMap;

    for(int i = 0; i < 30; i++)
    {
        varList << QVariant();
        varMap[i] = QVariant();
        varStrMap[QString::number(i)] = QVariant();
    }

    m_benchmark.clear();

    JZBENCHMARK(get_list)
    {
        QVariant &v = varList[3];
    }

    JZBENCHMARK(get_int_map)
    {
        QVariant &v = varMap[3];
    }

    QVariant v_int1,v_int2,v_int3;
    v_int1 = 0;
    v_int2 = 0;
    v_int3 = 0;

    int *p_int1 = (int*)v_int1.data();
    int *p_int2 = (int*)v_int2.data();
    int *p_int3 = (int*)v_int3.data();
    JZBENCHMARK(int_add)
    {
        *p_int3 = *p_int1 + *p_int2;
    }

    JZBENCHMARK(var_int_add)
    {
        v_int3 = v_int1.toInt() + v_int2.toInt();
    }

    JZBENCHMARK(get_str_map)
    {
        QVariant &v = varStrMap["3"];
    }   

    QVariant v = QString("242");
    JZBENCHMARK(variant_type)
    {
        int t = JZNodeType::variantType(v);
    }

    JZBENCHMARK(type_to_name)
    {
        QString t = env->typeToName(Type_timer);
    }

    JZBENCHMARK(name_to_type)
    {
        int t = env->nameToType("timer");
    }    

    m_benchmark.report();
}

void BenchmarkTest::testCall()
{
    return;
    if(!build())
        return;
    
    m_benchmark.clear();

    auto env = m_project.environment();
    auto pow_func = env->functionManager()->functionImpl("pow");
    JZBENCHMARK(jz_pow)
    {
        QVariantList in,out;
        in << 0.5 << 0.6;
        m_engine.call(pow_func,in,out);
    }

    JZBENCHMARK(jz_cfunc_pow)
    {
        QVariantList in,out;
        in << 0.5 << 0.6;
        pow_func->cfunc->call(in,out);
    }

    std::function<double(double,double)> func_pow = (double (*)(double,double))(pow);
    JZBENCHMARK(jz_cfunc_pow_std)
    {
        func_pow(0.5, 0.6);
    }

    //list_get
    auto obj = env->objectManager()->create(Type_intList);
    auto list = (QList<int>*)(obj->cobj());
    *list << 1 << 2 << 3 << 4 << 5;
    JZNodeObjectPointer ptr(obj,true);

    auto list_func = env->functionManager()->functionImpl("QList<int>::get");
    JZBENCHMARK(jz_list_get)
    {
        QVariantList in,out;
        in << QVariant::fromValue(ptr.toWeakPointer()) << 1;
        bool ret = m_engine.call(list_func,in,out);
        QVERIFY(ret);
    }

    JZBENCHMARK(c_list_get)
    {
        QVariant out = (*list)[1];
    }

    m_benchmark.report();
}

void BenchmarkTest::testSort()
{
    return;

    auto env = m_project.environment();
    JZFunctionDefine def;
    def.name = "testSort";
    def.paramIn.push_front(env->paramDefine("list",Type_intList));

    auto obj = m_objInst->create(Type_intList);
    QList<int> *list = JZObjectCast<QList<int>>(obj);

    JZNodeObjectPointer ptr(obj,true);
    QList<int> base_list;
    int list_len = 200;
    qsrand(150);
    for (int i = 0; i < list_len; i++)
    {
        int value = qrand();
        base_list << value;
        list->append(value);
    }

    auto script = m_file->addFunction(def);
    script->addLocalVariable("tmp", "int");

    auto start = script->getNode(0);

    JZNodeFor *for_i = new JZNodeFor();
    JZNodeFor *for_j = new JZNodeFor();

    JZNodeFunction *list_size = new JZNodeFunction();
    list_size->setFunction(m_funcInst->function("QList<int>::size"));

    JZNodeFunction *list_getI = new JZNodeFunction();
    list_getI->setFunction(m_funcInst->function("QList<int>::get"));

    JZNodeFunction *list_getJ = new JZNodeFunction();
    list_getJ->setFunction(m_funcInst->function("QList<int>::get"));

    JZNodeAdd *add = new JZNodeAdd();

    JZNodeIf *node_if = new JZNodeIf();
    JZNodeLT *node_lt = new JZNodeLT();

    JZNodeParam *param_list = new JZNodeParam();
    param_list->setVariable("list");

    JZNodeParam *param_tmp = new JZNodeParam();
    param_tmp->setVariable("tmp");

    JZNodeSetParam *set_tmp = new JZNodeSetParam();
    JZNodeFunction *set_i = new JZNodeFunction();
    JZNodeFunction *set_j = new JZNodeFunction();
    set_tmp->setVariable("tmp");
    set_i->setFunction(m_funcInst->function("QList<int>::set"));
    set_j->setFunction(m_funcInst->function("QList<int>::set"));

    script->addNode(for_i);
    script->addNode(for_j);
    script->addNode(add);
    script->addNode(param_tmp);
    script->addNode(param_list);
    script->addNode(list_size);
    script->addNode(list_getI);
    script->addNode(list_getJ);
    script->addNode(set_tmp);
    script->addNode(set_i);
    script->addNode(set_j);
    script->addNode(node_if);
    script->addNode(node_lt);

    script->addConnect(param_list->paramOutGemo(0),set_i->paramInGemo(0));
    script->addConnect(for_i->paramOutGemo(0),set_i->paramInGemo(1));
    script->addConnect(param_list->paramOutGemo(0),set_j->paramInGemo(0));
    script->addConnect(for_j->paramOutGemo(0),set_j->paramInGemo(1));

    script->addConnect(list_getI->paramOutGemo(0),node_lt->paramInGemo(0));
    script->addConnect(list_getJ->paramOutGemo(0),node_lt->paramInGemo(1));
    script->addConnect(node_lt->paramOutGemo(0),node_if->paramInGemo(0));

    script->addConnect(param_list->paramOutGemo(0),list_getI->paramInGemo(0));
    script->addConnect(for_i->paramOutGemo(0),list_getI->paramInGemo(1));
    script->addConnect(param_list->paramOutGemo(0),list_getJ->paramInGemo(0));
    script->addConnect(for_j->paramOutGemo(0),list_getJ->paramInGemo(1));

    script->addConnect(start->flowOutGemo(0),for_i->flowInGemo());
    script->addConnect(for_i->paramOutGemo(0),add->paramInGemo(0));
    add->setParamInValue(1,"1");
    script->addConnect(add->paramOutGemo(0),for_j->paramInGemo(0)); 

    script->addConnect(param_list->paramOutGemo(0),list_size->paramInGemo(0));

    script->addConnect(list_size->paramOutGemo(0),for_i->paramInGemo(2));
    script->addConnect(list_size->paramOutGemo(0),for_j->paramInGemo(2));
    
    script->addConnect(for_i->subFlowOutGemo(0),for_j->flowInGemo());

    script->addConnect(for_j->subFlowOutGemo(0),node_if->flowInGemo());

    script->addConnect(list_getJ->paramOutGemo(0),set_tmp->paramInGemo(1));
    script->addConnect(list_getI->paramOutGemo(0),set_j->paramInGemo(2));
    script->addConnect(param_tmp->paramOutGemo(0),set_i->paramInGemo(2));

    script->addConnect(node_if->subFlowOutGemo(0),set_tmp->flowInGemo());
    script->addConnect(set_tmp->flowOutGemo(0),set_j->flowInGemo());
    script->addConnect(set_j->flowOutGemo(0),set_i->flowInGemo());

    if(!build())
        return;
    dump("testSort");
    
    m_benchmark.clear();

    m_engine.statClear();
    JZBENCHMARK(jz_sort)
    {
        m_engine.statClear();

        QVariantList in,out;
        in << QVariant::fromValue(ptr);
        bool ret = m_engine.call("testSort",in,out);
        QVERIFY2(ret, qUtf8Printable(m_engine.runtimeError().errorReport()));
    }
    m_engine.statReport();

    m_engine.statClear();
    m_engine.setDebug(true);
    JZBENCHMARK(jz_sort_debug)
    {
        m_engine.statClear();

        QVariantList in, out;
        in << QVariant::fromValue(ptr);
        bool ret = m_engine.call("testSort", in, out);
        QVERIFY2(ret, qUtf8Printable(m_engine.runtimeError().errorReport()));
    }
    m_engine.statReport();

    auto clist_get = [](const QVariantList &vlist, int idx)->QVariant
    {
        return vlist[idx];
    };

    auto clist_set = [](QVariantList &vlist, int idx,const QVariant &value)
    {
        vlist[idx] = value;
    };


    QList<int> c_list;
    JZBENCHMARK(c_sort)
    {
        c_list = base_list;
        for(int i = 0; i < c_list.size(); i++)
        {
            for(int j = i+1; j < c_list.size(); j++)
            {
                if(c_list[i] < c_list[j])
                {
                    int tmp = c_list[j];
                    c_list[j] = c_list[i];
                    c_list[i] = tmp;
                }
            }
        }
    }
    m_benchmark.report();
}

void BenchmarkTest::testSum()
{
    return;

    auto env = m_project.environment();
    JZFunctionDefine define;
    define.name = "testSum";     
    define.paramIn.push_back(env->paramDefine("count", Type_int));
    define.paramOut.push_back(env->paramDefine("result", Type_int64));

    JZScriptItem *script = m_file->addFunction(define);
    script->addLocalVariable(env->paramDefine("sum", Type_int64));

    JZNode *node_start = script->getNode(0);

    JZNodeFor *node_for = new JZNodeFor();
    JZNodeAdd *node_add = new JZNodeAdd();
    JZNodeParam *node_sum = new JZNodeParam();
    JZNodeParam *node_number = new JZNodeParam();
    JZNodeSetParam *node_set = new JZNodeSetParam();
    JZNodeReturn *node_ret = new JZNodeReturn();
    node_ret->setFunction(&define);

    int start_id = node_start->id();
    int for_id = script->addNode(node_for);
    int set_id = script->addNode(node_set);
    script->addNode(node_add);
    script->addNode(node_sum);
    script->addNode(node_ret);
    script->addNode(node_number);

    node_sum->setVariable("sum");
    node_set->setVariable("sum");
    node_number->setVariable("count");

    //start
    script->addConnect(JZNodeGemo(start_id, node_start->flowOut()), JZNodeGemo(for_id, node_for->flowIn()));

    script->addConnect(node_number->paramOutGemo(0), node_for->paramInGemo(2));
    script->addConnect(node_for->subFlowOutGemo(0), node_set->flowInGemo());
    script->addConnect(node_sum->paramOutGemo(0), node_add->paramInGemo(0));
    script->addConnect(node_for->paramOutGemo(0), node_add->paramInGemo(1));
    script->addConnect(node_add->paramOutGemo(0), node_set->paramInGemo(1));

    script->addConnect(node_for->flowOutGemo(0), node_ret->flowInGemo());
    script->addConnect(node_sum->paramOutGemo(0), node_ret->paramInGemo(0));

    if(!build())
        return;

    m_benchmark.clear();

    auto c_sum = [](int num)->qint64{
        qint64 sum = 0;
        for(int i = 0; i < num; i++)
            sum += i;
        return sum;
    };

    int time = 100000;
    qint64 jz_sum_count = 0;
    JZBENCHMARK(jz_sum)
    {
        QVariantList in,out;
        in << time;
        m_engine.call("testSum",in,out);
        jz_sum_count = out[0].toLongLong();
    }
    QCOMPARE(jz_sum_count,c_sum(time));
    m_engine.statReport();

    JZBENCHMARK(jz_sum_var)
    {   
        QVariant sum = (qint64)0;
        QVariant index = 0;
        QVariant step = 1;
        QVariant end = time;

        while(index.toInt() < end.toInt())
        {
            sum = m_engine.dealExpr(sum,index,OP_add);
            index = m_engine.dealExpr(index,step,OP_add);
        }
        jz_sum_count = sum.toLongLong();
    }
    QCOMPARE(jz_sum_count,c_sum(time));

    m_benchmark.report();
} 

void BenchmarkTest::testTryCatch()
{
    JZBENCHMARK(test_no_throw)
    {
        try {
            msleep(1);
        }
        catch (const std::exception& e)
        {
        }
    }

    JZBENCHMARK(test_throw)
    {
        try {
            msleep(1);
            throw std::runtime_error("test");
        }
        catch (const std::exception& e)
        {
        }
    }

    m_benchmark.report();
}

void test_benchmark(int argc, char *argv[])
{
    BenchmarkTest test;
    QTest::qExec(&test,argc,argv);
}